

package ficheros;

import java.util.Hashtable;

public class lexicoprueba {
     TRABAJOFICHEROS fich = new TRABAJOFICHEROS("c:/prueba.txt",0);
     private char car;
     private int columnafich,filafich,estado;
     private int filaToc,columnaToc;
     private String lexema,lexemaAnt;
     private int numToken;
     private Hashtable listaTokens;
     private Hashtable listapalRes;
     private Hashtable listapalConv;
     private GestorErrores gestErr;

     //también añadimos un array con información sobre los distintos errores que se pueden producir en el léxico
     private static final String mensajesError[] = {
        "ERROR LÉXICO : Carácter no esperado",  //0
        "ERROR LÉXICO : Parte decimal mal formada", //1
        "ERROR LÉXICO : Parte exponencial mal formada", //2
        "ERROR LÉXICO : Constante carácter mal formada",  //3
        "ERROR LÉXICO : Operador lógico mal formado",    //4
        "ERROR LÉXICO : Número entero mal formado"    //5
    };

    public lexicoprueba()
     {
        this.gestErr=new GestorErrores();
        this.listaTokens=new Hashtable();
        this.listapalRes=new Hashtable();
        this.listapalConv=new Hashtable();
        this.columnafich=0;
        this.filafich=0;
        this.estado=0;
        this.lexema="";
        tablaPalRes();
        tablaPalConv();
     }
    
     private void tablaPalRes()
     {
        listapalRes.put("true", "");
        listapalRes.put("false","");
        listapalRes.put("int","");
        listapalRes.put("real","");
        
     }
     private void tablaPalConv()
     {
        listapalConv.put("float","");
        this.listapalConv.put("int","");
     }
     public  void escaner()
     {
         this.car=fich.siguienteCar();
         //incrementamos la fila y columna del fichero por la que vamos
         columnafich++;filafich++;

         while (fich.getBuff() != -1)
        {
           anLex();
        }
         //esta es una parida que me he inventado para que llame al final al analizador léxico otra vez
         //con el último token cogido
        
         anLex();
          fich.cierraArchivo();
     }
      private void anLex()
     {
        Token token;
        switch (this.estado)
        {
            case (0):
                //estado inicial a partir del que se empiezan a reconocer los tokens
                //almaceno fila y columna del inicio del token actual
                this.filaToc=this.filafich;
                this.columnaToc=this.columnafich;
                if ((this.car == '\t') || (this.car == '\r') || (this.car == '\n') || this.car == ' ')
                {
                    this.transita(0);
                    //si lo que lee es un salto o blanco o...el lexema tiene que estar en blanco..
                    this.lexema="";
                }else {//en el caso de q no estemos en el estado 0 según lo q valga el caracter estaremos en un estado u otro
                switch (this.car)
                {
                                        
                    case '=':
                        transita(11);
                        break;
                    case '<':
                        transita(14);
                        break;
                    case '>':
                        transita(17);
                        break;
                    case '!':
                        transita(20);
                        break;
                    case '|':
                        transita(23);
                        break;
                    case '+':
                        transita(25);
                        break;
                    case '-':
                        transita(26);
                        break;
                    case '*':
                        transita(27);
                        break;
                    case '/':
                        transita(28);
                        break;
                    case '%':
                        transita(29);
                        break;
                    case ';':
                        transita(30);
                        break;
                    case '&':
                        transita(31);
                        break;
                    case '(':
                        transita(33);
                        break;
                    case ')':
                        transita(34);
                        break;
                    default:
                        if ((Character.isLetter(car))||(car=='_'))
                        {
                            transita(9);
                        }
                        else
                            if (Character.isDigit(car))
                            {
                              transita(1);
                            }
                            else
                            {this.tratarError(0);
                            transita(0);}
                        break;
                }
                     }
                break;
            case (1)://dígitos
                if (Character.isDigit(car))
                {
                    transita(1);
                }
                    else if (car=='.')
                    {
                        transita(3);
                    }else
                    {
                        //podría darse el caso que tengamos el literal entero 000000000 llamo a quitarCeros
                        //utilizo el lexemaAnt porque hay que retroceder, así en lexeman estará el último lexema 
                    //ya que al llamar a transita(2) lee otro caracter
                        this.lexemaAnt=this.quitarCeros(this.lexema);
                        transita(2);
                    }
                break;
            case (2):
                
                token=new Token("entero",this.lexemaAnt,this.filaToc,this.columnaToc);
                this.lexemaAnt="";
                this.devuelveToken(token);
                break;
            case (3):
                if (Character.isDigit(car))
                {
                 transita(4);
                }
                else
                {
                 this.tratarError(0);//carácter inesperado
                }
                break;
            case (4):
                if (Character.isDigit(car))
                {
                    transita(4);
                }
                else if ((car=='E')||(car=='e'))
                {   //si encuentra un e o E quito los posibles 0s q haya a la izqu o derecha del número real y
                    //transita, entonces un 000034.340000 será un 34.34 y al transitar al 6 une el e y queda 34.34e
                    lexema=this.quitaCerosReal(lexema);
                    transita(6);
                }
                else
                { //ha leído cualquier cosa hay q devolver el token real en el case(5)
                    //pero hay que quedarse con el lexema anterior porque será el real
                    
                    this.lexemaAnt=this.lexema;
                    //la función de abajo no vale porque te pone exponente y redondea
                    lexemaAnt=this.quitaCerosReal(lexemaAnt);
                    transita(5);//como en transita lee otro caracter
                }
                    break;
            case (5):
                token=new Token("real",this.lexemaAnt,this.filaToc,this.columnaToc);
                this.lexemaAnt="";
                this.devuelveToken(token);
                break;
            case (6):
                if (Character.isDigit(car)&& car!='0')
                {
                    transita(8);
                }else if (car=='-')
                {
                    transita(7);
                }
                else
                {
                    this.tratarError(0);
                }
            case (7):
            {
                if (Character.isDigit(car))
                {
                    transita(8);
                }
                else this.tratarError(0);
            }
            case (8):
            {
                if (Character.isDigit(car))
                {
                    transita(8);
                }
                else
                {//pasa lo mismo que en el ultimo caso del 4. Ha leído el lexema "cualquier cosa" necesito
                    this.lexemaAnt=this.lexema;
                    transita(5);
                }

            }
            break;
            case (9):
            {
                if (Character.isLetter(car)||(car=='_')||Character.isDigit(car))
                {
                    transita(9);
                }
                else
                {//devolvemos el token, pero como antes hay que devolver el token antes de llamar a transita(10)
                    //que meterá un carácter más en le lexema que no nos interesa
                    this.lexemaAnt=this.lexema;
                    transita(10);
                }
            }
            break;
            case (10):
            {
               //NECESITO UNA VARIABLE AUXILIAR PARA CONVERTIRLA A MINÚSCULA Y PODER MIRAR EL HAST
               String aux=this.lexemaAnt.toLowerCase();
               if (this.listapalRes.containsKey(aux))
                    {
                       token=new Token("palRes",aux,this.filaToc,this.columnaToc);
                       this.lexemaAnt="";
                    }else
                    {
                       token=new Token("identificador",this.lexemaAnt,this.filaToc,this.columnaToc);
                       this.lexemaAnt="";
                    }
                    this.devuelveToken(token);
                    break;
            }
            case (11):
            {
              if (car=='=')
             {
                transita(12);
             }
              else
              {//antes de transitar guardamos el léxema
                this.lexemaAnt=this.lexema;
                transita(13);
              }
            }
            break;
            case (12)://caso de que sean dos ==
            {
                token=new Token(this.lexema,"",this.filaToc,this.columnaToc);
                this.devuelveToken(token);               
                break;
            }
            case (13)://caso de que sea un sólo =
            {
                token=new Token(this.lexemaAnt,"",this.filaToc,this.columnaToc);
                this.devuelveToken(token);
                this.lexemaAnt="";
                break;
            }
            case (14):
             {
              if (car=='=')
             {
                transita(15);
             }
              else
              {//antes de transitar guardamos el léxema
                this.lexemaAnt=this.lexema;
                transita(16);
              }
            }
            break;
             case (15)://caso de que sea <=
            {
                token=new Token(this.lexema,"",this.filaToc,this.columnaToc);
                this.devuelveToken(token);

                break;
            }
            case (16)://caso de que sea <
            {
                token=new Token(this.lexemaAnt,"",this.filaToc,this.columnaToc);
                this.lexemaAnt="";
                this.devuelveToken(token);
                break;
            }
            case (17):
             {
              if (car=='=')
             {
                transita(18);
             }
              else
              {//antes de transitar guardamos el léxema
                this.lexemaAnt=this.lexema;
                transita(19);
              }
            }
            break;
             case (18)://caso de que sea >=
            {
                token=new Token(this.lexema,"",this.filaToc,this.columnaToc);
                this.devuelveToken(token);
                break;
            }
            case (19)://caso de que sea >
            {
                token=new Token(this.lexemaAnt,"",this.filaToc,this.columnaToc);
                this.lexemaAnt="";
                this.devuelveToken(token);
                break;
            }
            case (20):
            {
                if (car=='=')
                {
                    transita(21);
                }
                else
                {
                    this.lexemaAnt=this.lexema;
                    transita(22);
                }
            }
            break;
            case (21)://caso de que sea !=
            {
                token=new Token(this.lexema,"",this.filaToc,this.columnaToc);
                this.devuelveToken(token);
                break;
            }
            case (22)://caso de que sea !
            {
                token=new Token(this.lexemaAnt,"",this.filaToc,this.columnaToc);
                this.lexemaAnt="";
                this.devuelveToken(token);
                break;
            }
            case (23):
            {
                if (car=='|')
                {
                    transita(24);
                }
                else
                    this.tratarError(4);
            }
            break;
            case (24):
                token=new Token(this.lexema,"",this.filaToc,this.columnaToc);
                this.devuelveToken(token);
                break;
            case (25):
                token=new Token(this.lexema,"",this.filaToc,this.columnaToc);
                this.devuelveToken(token);
                break;
            case (26):
                token=new Token(this.lexema,"",this.filaToc,this.columnaToc);
                this.devuelveToken(token);
                break;
            case (27):
                token=new Token(this.lexema,"",this.filaToc,this.columnaToc);
                this.devuelveToken(token);
                break;
             case (28):
                token=new Token(this.lexema,"",this.filaToc,this.columnaToc);
                this.devuelveToken(token);
                break;
            case (29):
                token=new Token(this.lexema,"",this.filaToc,this.columnaToc);
                this.devuelveToken(token);
                break;
            case (30):
                token=new Token(this.lexema,"",this.filaToc,this.columnaToc);
                this.devuelveToken(token);
                break;
            case (31):
            {
                if (car=='&')
                {
                    transita(32);
                }
                else
                    this.tratarError(4);
            }
            break;
            case (32):
                token=new Token(this.lexema,"",this.filaToc,this.columnaToc);
                this.devuelveToken(token);
                break;
            case (33):
                token=new Token(this.lexema,"",this.filaToc,this.columnaToc);
                this.devuelveToken(token);
                break;
            case (34):
                token=new Token(this.lexema,"",this.filaToc,this.columnaToc);
                this.devuelveToken(token);
                break;
                default:
                        transita(0);
                break;
           }                
        }
        
      private void transita(int estado)
      {
        String aux=String.valueOf(car);
        this.lexema=this.lexema + aux;
        //hay que incrementar la fila o la columna según lo que toque ya que hemos leído un nuevo caracter
        if (car=='\n')
        {
            //actualizo el número de fila e inicializo el número de columna
            this.filafich++;
            this.columnafich=1;
        }
         else
         {
            this.columnafich++;
         }
         //leo el siguiente caracter y acutalizo el número de columna
        car=fich.siguienteCar();
        this.estado=estado;
        }

      private void devuelveToken(Token token)
      {
          //token generado
        String datostoken = token.toString();
        System.out.println(datostoken);
        this.listaTokens.put(this.numToken,token);
        this.numToken++;
        this.lexema="";
        this.estado=0;
      }
      private String quitaCerosReal(String lex)
      {
        int posicion = lex.indexOf(".");
  	 if (posicion!=-1){
    	 String substring1 = lex.substring(0,posicion);
    	 String substring2 = lex.substring(posicion + 1);
         String entera = this.quitarCeros(substring1);
         String decimal = this.quitarCeros(substring2);
         return entera + "." + decimal;
         }else
         {
            return "";
         }
      }
      private String quitarCeros(String lex)
      {
          String aux;
          //podría ser el lexema 000044432 hay que quitar los ceros a la izquierda
          //también podría darse el caso de 0000000 habría que devolver 0
          int numero;
          numero=Integer.parseInt(lex);
          aux=String.valueOf(numero);
          return aux;
      }
       private void tratarError (int codError){
        this.estado = 0;
        this.lexema = "";
        error (codError);
    }
       private void error (int codError){
        String sCadena = "Linea: " + this.filaToc + " Col: " +
                this.columnaToc + " " + mensajesError[codError];
        //Anotamos el error en la tabla de errores del GestorErrores
        this.gestErr.escribeError(sCadena);

    }
}
